package com.tools.common.object;

import com.tools.common.container.OwnPairs;
import com.tools.common.exception.ReflectionException;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.framework.AopContext;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * 反射操作的工具类
 * */
@Note("反射操作的工具类")
class ReflexKit {

    ReflexKit() {}

    /* ************************************************************************************************************
     *
     *
     *          反射获取某些内容
     *
     * ************************************************************************************************************
     * */


    @Note("根据全限定类名获取对应的字节码对象")
    public static Class<?> getObjectClass(String classname) {
        Class<?> objClass;
        try {
            objClass = Class.forName(classname);
        } catch (ClassNotFoundException e) {
            Class<?> basicClass = BasicEnum.getBasicClassByNameSafe(classname);
            if(basicClass != null) {
                return basicClass;
            }
            throw new IllegalArgumentException("没有找到字节码: " + classname);
        }
        return objClass;
    }





    @Note("根据字节码对象来获取类的包路径")
    public static String getPackagePath(Class<?> objClass) {
        if(objClass == null) return "";
        Package p = objClass.getPackage();
        return (p == null) ? "" : p.getName();
    }


    @Note("重载：根据实体对象获取字节码来获取类的包路径")
    public static <T>String getPackagePath(T obj) {
        if(obj == null) return "";
        return getPackagePath(obj.getClass());
    }


    @Note("获取无参构造器")
    public static <T>Constructor<T> getConstructor(Class<T> objClass) {
        if(objClass == null) throw new NullPointerException("参数 [objClass] 不能为 null");
        Constructor<T> con;
        try {
            con = objClass.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("【" + objClass.getName() + "】 不存在无参构造方法");
        }
        con.setAccessible(true);
        return con;
    }


    @Note("根据参数类型列表来推断获取哪个构造器返回")
    public static <T> Constructor<T> getConstructor(Class<T> objClass, Class<?>... parameterTypes) {
        if(objClass == null) throw new NullPointerException("参数 [objClass] 不能为 null");
        if(parameterTypes == null || parameterTypes.length == 0) return getConstructor(objClass);
        Constructor<T> con;
        try {
            con = objClass.getDeclaredConstructor(parameterTypes);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("【" + objClass.getName() + "】 不存在相应的构造方法，请检查参数类型列表或目标类");
        }
        con.setAccessible(true);
        return con;
    }


    @Note("重载：根据字节码封装对象 BC 内部的字节码对象和参数类型数组来推断该返回哪个构造器")
    public static <T> Constructor<T> getConstructor(C<T> c) {
        if(c == null) throw new NullPointerException("参数 [objClass] 不能为 null");
        Class<T> objClass = c.getMainType();
        Class<?>[] argTypeArray = c.argTypeArray();
        Constructor<T> constructor;
        try{
            constructor = objClass.getDeclaredConstructor(argTypeArray);
        } catch (Exception e) {
            throw new IllegalArgumentException("【" + objClass.getName() + "】 不存在相应的构造方法，请检查参数类型列表或目标类");
        }
        constructor.setAccessible(true);
        return constructor;
    }



    @Note("获取目标类的无参构造器，如不存在则获取其他被 public 修饰的构造器中的任意一个，若都不存在则返回 null")
    public static Constructor<?> getUsefulConstructor(C<?> c) {
        try {
            return getConstructor(c);
        } catch (Exception e) {
            Class<?> objClass = c.getMainType();
            Constructor<?>[] publicConstructors = objClass.getConstructors();
            if(publicConstructors.length != 0) {
                return publicConstructors[0];
            }
            Constructor<?>[] allConstructors = objClass.getDeclaredConstructors();
            return (allConstructors.length == 0) ? null : allConstructors[0];
        }
    }



    @Note("获取字节码所在类的所有构造器，并取消权限检查")
    public static <T> Constructor<T>[] getAllConstructor(Class<T> objClass) {
        Constructor<?>[] constructors = objClass.getDeclaredConstructors();
        for(Constructor<?> c : constructors) {
            c.setAccessible(true);
        }
        return (Constructor<T>[]) constructors;
    }



    @Note("获取构造器所有形参的名称")
    public static String[] getConstructorArgNames(Constructor<?> constructor) {
        if(constructor == null) return new String[0];
        Parameter[] parameters = constructor.getParameters();
        String[] names = new String[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            names[i] = parameters[i].getName();
        }
        return names;
    }


    @Note("获取当前对象的代理对象")
    public static Object currentProxy(Object obj) {
        return AopContext.currentProxy();
    }



    @Note("获取被代理的对象的字节码对象")
    public static Class<?> getProxyTargetClass(Object obj) {
        if(obj == null) {
            throw new IllegalArgumentException("代理对象 [obj] 不能为 null");
        }
        Class<?> result = null;
        if (obj instanceof TargetClassAware) {
            result = ((TargetClassAware)obj).getTargetClass();
        }
        if (result == null) {
            result = isCglibProxy(obj) ? obj.getClass().getSuperclass() : obj.getClass();
        }
        return result;
    }

    /* *******************************************************************************************
     *
     *          属性操作
     *
     * *******************************************************************************************
     * */

    @Note("获取字段对象，包括父类的字段，无视权限修饰符限制")
    public static Field getField(Class<?> objClass, String fieldName) {
        Class<?> currentClass = objClass;
        while (currentClass != null) {
            try {
                Field field = currentClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
                currentClass = currentClass.getSuperclass();
                if (currentClass == null || Object.class.equals(currentClass)) {
                    throw new ReflectionException(e);
                }
            }
        }
        throw new ReflectionException("没有找到 " + fieldName + " 字段");
    }


    @Note("通过反射的方式获取目标对象的属性值")
    public static Object getFieldValue(Object obj, Field field) {
        if(field == null || obj == null) {
            return null;
        }
        try {
            return field.get(obj);
        } catch (IllegalAccessException e) {
            throw new ReflectionException(e);
        }
    }

    public static Object getFieldValue(Object obj, String fieldName) {
        return getFieldValue(obj.getClass(), obj, fieldName);
    }

    public static Object getFieldValue(Class<?> objClass, Object obj, String fieldName) {
        Field field = getField(objClass, fieldName);
        return getFieldValue(obj, field);
    }


    @Note("通过反射的方式给目标对象的属性设置属性值")
    public static void setFieldValue(Object obj, Field field, Object value) {
        if(field == null || obj == null) {
            return;
        }
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            throw new ReflectionException(e);
        }
    }


    public static void setFieldValue(Object obj, String fieldName, Object value) {
        setFieldValue(obj.getClass(), obj, fieldName, value);
    }

    public static void setFieldValue(Class<?> objClass, Object obj, String fieldName, Object value) {
        Field field = getField(objClass, fieldName);
        setFieldValue(obj, field, value);
    }


    @Note("获取目标 Java 类和其父类的所有属性（取消了权限检查，当然其中排除了 Object 类的所有属性）")
    public static Map<String, Field> allFields(Class<?> objClass) {
        HashMap<String, Field> result = new HashMap<>();
        if(objClass == null) return result;
        Class<?> superclass = objClass.getSuperclass();
        while (superclass != null && !superclass.equals(Object.class)) {
            Field[] parents = superclass.getDeclaredFields();
            for(Field f : parents) {
                f.setAccessible(true);
                String name = f.getName();
                if(result.containsKey(name)) {
                    continue;
                }
                result.put(name, f);
            }
            superclass = superclass.getSuperclass();
        }
        Field[] fields = objClass.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            result.put(f.getName(), f);
        }
        return result;
    }


    @Note("获取目标实例对象自己、和其父类的所有【属性名称-属性值】的 HashMap 集合（取消了权限检查，当然其中排除了 Object 类的所有属性）")
    public static Map<String, Object> allFieldValue(Object obj) {
        Map<String, Object> result = new HashMap<>();
        addAllFieldValue(result, obj);
        return result;
    }

    @Note("获取 target 对象自己和和其父类的所有【属性名称-属性值】，拷贝到 src 里")
    public static void addAllFieldValue(Map<String, Object> src, Object target) {
        addAllFieldValue(src, target, null);
    }

    @Note("获取 target 对象自己和和其父类的所有【属性名称-属性值】，拷贝到 src 里。拷贝数据之前会对 JavaBean 属性名称和属性值做一些自定义增强操作")
    public static void addAllFieldValue(Map<String, Object> src, Object target, ForcePair<String, Object> force) {
        if(src == null || target == null) return;
        Class<?> objClass = target.getClass();
        Class<?> superclass = objClass.getSuperclass();
        if(force == null) {
            while (superclass != null && !superclass.equals(Object.class)) {
                Field[] parents = superclass.getDeclaredFields();
                for(Field f : parents) {
                    f.setAccessible(true);
                    String name = f.getName();
                    if(src.containsKey(name)) {
                        continue;
                    }
                    src.put(name, getFieldValue(target, f));
                }
                superclass = superclass.getSuperclass();
            }
            Field[] fields = objClass.getDeclaredFields();
            for (Field f : fields) {
                f.setAccessible(true);
                src.put(f.getName(), getFieldValue(target, f));
            }
            return;
        }
        while (superclass != null && !superclass.equals(Object.class)) {
            Field[] parents = superclass.getDeclaredFields();
            for(Field f : parents) {
                f.setAccessible(true);
                String name = f.getName();
                if(src.containsKey(name)) {
                    continue;
                }
                Map.Entry<String, Object> e = force.force(name, getFieldValue(target, f));
                if(e == null) continue;
                src.put(e.getKey(), e.getValue());
            }
            superclass = superclass.getSuperclass();
        }
        Field[] fields = objClass.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            Map.Entry<String, Object> e = force.force(f.getName(), getFieldValue(target, f));
            if(e == null) continue;
            src.put(e.getKey(), e.getValue());
        }
    }


    @Note("获取 target 对象自己和和其父类的所有【属性名称-属性值】，拷贝到 src 里")
    public static void addAllFieldValue(OwnPairs<String, Object> src, Object target) {
        addAllFieldValue(src, target, null);
    }


    @Note("获取 target 对象自己和和其父类的所有【属性名称-属性值】，拷贝到 src 里。拷贝数据之前会对 JavaBean 属性名称和属性值做一些自定义增强操作")
    public static void addAllFieldValue(OwnPairs<String, Object> src, Object target, ForcePair<String, Object> force) {
        if(src == null || target == null) return;
        Class<?> objClass = target.getClass();
        Field[] fields = objClass.getDeclaredFields();
        if(force == null) {
            Class<?> superclass = objClass.getSuperclass();
            while (superclass != null && !superclass.equals(Object.class)) {
                for(Field f : superclass.getDeclaredFields()) {
                    f.setAccessible(true);
                    String name = f.getName();
                    if(!src.containsKey(name)) {
                        src.put(name, getFieldValue(target, f));
                    }
                }
                superclass = superclass.getSuperclass();
            }
            for (Field f : fields) {
                f.setAccessible(true);
                src.put(f.getName(), getFieldValue(target, f));
            }
            return;
        }
        Class<?> superclass = objClass.getSuperclass();
        while (superclass != null && !superclass.equals(Object.class)) {
            for(Field f : superclass.getDeclaredFields()) {
                f.setAccessible(true);
                String name = f.getName();
                if(!src.containsKey(name)) {
                    Map.Entry<String, Object> e = force.force(name, getFieldValue(target, f));
                    if(e == null) continue;
                    src.put(e.getKey(), e.getValue());
                }
            }
            superclass = superclass.getSuperclass();
        }
        for (Field f : fields) {
            f.setAccessible(true);
            Map.Entry<String, Object> e = force.force(f.getName(), getFieldValue(target, f));
            if(e != null) src.put(e.getKey(), e.getValue());
        }
    }

    /* *******************************************************************************************
     *
     *          方法操作
     *
     * *******************************************************************************************
     * */


    @Note("反射执行方法，且返回方法的返回值")
    public static Object invoke(Method method, Object obj, Object... args) {
        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw new ReflectionException(e);
        }
    }


    @Note("反射获取指定的目标方法")
    public static Method getMethod(Class<?> objClass, String name, Class<?>... argTypes) {
        if(objClass == null) {
            throw new ReflectionException("没有找到目标方法，请检查该类与其父类是否存在目标方法");
        }
        return private_getMethod(objClass, name, argTypes);
    }


    @Note("反射获取指定的属性的 setter 方法")
    public static Method getSetter(Class<?> objClass, String fieldName, Class<?>... argTypes) {
        if(objClass == null) throw new NullPointerException("目标字节码对象为 null");
        if(fieldName == null || fieldName.isEmpty()) throw new NullPointerException("属性字段名称为空");
        String newFieldName = "set" +
                BasicEnum.CHAR.toUpper(fieldName.charAt(0)) +
                fieldName.substring(1);
        return private_getMethod(objClass, newFieldName, argTypes);
    }


    @Note("notIs 为 false 则名称默认以 is 开头，反射获取指定的属性的 getter 方法")
    public static Method getGetter(Class<?> objClass, String fieldName, boolean notIs, Class<?>... argTypes) {
        if(objClass == null) throw new NullPointerException("目标字节码对象为 null");
        if(fieldName == null || fieldName.isEmpty()) throw new NullPointerException("属性字段名称为空");
        String newFieldName =
                (notIs ? "get" : "is") +
                        BasicEnum.CHAR.toUpper(fieldName.charAt(0)) +
                        fieldName.substring(1);
        return private_getMethod(objClass, newFieldName, argTypes);
    }

    /* *******************************************************************************************
     *
     *          反射构造实体对象
     *
     * *******************************************************************************************
     * */


    @Note("反射构建枚举类对象")
    public static Object buildEnum(Class enumClass, String enumName) {
        if (enumClass == null) return null;
        if (Enum.class.isAssignableFrom(enumClass)) {
            return Enum.valueOf(enumClass, enumName);
        }
        throw new ReflectionException("反射构建枚举实例失败，因为其 【" + enumClass.getName() + "】 不属于枚举类型");
    }


    @Note("用字节码对象构造一个无参的实例对象（前提要存在无参构造器）")
    public static <T>T buildObject(Class<T> objClass) {
        if(objClass == null) throw new NullPointerException("参数 objClass 不能为 null");
        T obj;
        try {
            Constructor<T> constructor = getConstructor(objClass);
            obj = constructor.newInstance();
        } catch (Exception e) {
            throw new ReflectionException("使用反射创建对象失败，请检查目标类是否有无参构造方法");
        }
        return obj;
    }


    @Note("使用反射指定构造器构造一个实例对象")
    public static <T>T buildObject(Class<T> objClass, Class<?>[] argTypes, Object... argValues) {
        if(objClass == null) throw new NullPointerException("参数 objClass 不能为 null");
        T obj;
        try {
            Constructor<T> constructor = getConstructor(objClass, argTypes);
            obj = constructor.newInstance(argValues);
        } catch (Exception e) {
            throw new ReflectionException("使用反射创建对象失败，请检查目标类是否有相应的构造方法");
        }
        return obj;
    }


    @Note("通过构造器和构造方法的参数值（如果有的话）构造一个实例对象")
    public static <T>T buildObject(Constructor<T> con, Object... argValues) {
        if(con == null) return null;
        if(argValues == null) argValues = new Object[0];
        T obj;
        try {
            obj = con.newInstance(argValues);
        } catch (Exception e) {
            throw new IllegalArgumentException("反射构建对象失败，请检查构造器参数列表是否与参数值匹配");
        }
        return obj;
    }


    @Note("根据字节码包装的形参和实参获取对应的构造器，构造实例对象")
    public static <T>T buildObject(C<T> c) {
        if(c == null) throw new NullPointerException("目标字节码包装对象为 null");
        Constructor<T> constructor = getConstructor(c);
        return buildObject(constructor, c.argValueArray());
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("递归获取方法对象的逻辑，从自己开始一直往父类身上找")
    private static Method private_getMethod(Class<?> objClass, String name, Class<?>... argTypes) {
        Method method;
        try {
            method = objClass.getDeclaredMethod(name, argTypes);
        } catch (NoSuchMethodException e) {
            method = getMethod(objClass.getSuperclass(), name, argTypes);
        }
        method.setAccessible(true);
        return method;
    }

    /* *******************************************************************************************
     *
     *          反射相关判断操作区域
     *
     * *******************************************************************************************
     * */

    @Note("判断目标对象是不是 cglib 的代理对象")
    public static boolean isCglibProxy(Object obj) {
        return (obj instanceof SpringProxy) && obj.getClass().getName().contains("$$");
    }
}
